Polymorphic Executables

Prelude

I was recommended this video on YouTube, and I found it very interesting and useful, so here I am writing about it. But what are polymorphic executables, you may ask? Polymorphic executables are scripts or programs that can change their behavior based on how they're invoked. This means creating a script that checks its own invocation name ($0 first argument in the executable which is the executable itself, more here) and performs different actions accordingly.

Batman and Robin

I think it will be easier if I show you how it works. So here is an example of how this executable is polymorphic:

1. First, create the script

#!/bin/bash
# use basename to not have to deal with paths
case $(basename "$0") in
    batman)
        echo "I'm Batman!";
        ;;
    robinDG)
       echo "And I'm robin (dick grayson)";
       ;;
    robinTD)
       echo "And I'm robin (tim drake)";
       ;;
    robinDW)
        echo "And I'm robin (damian wayne)";
        ;;
    *)
        echo "catch all [ $0 ]!"
        ;;
esac

Here we use a code to check the value of $0 . Depending on that name, it outputs a different message.

2. Make it executable

$ chmod +x batman

3. When we run it directly, we get

$ ./batman
I'm Batman! 

4. Now, let's create symbolic links to the same script but with different names

$ ln -s batman robinDG
$ ln -s batman robinTD
$ ln -s batman robinDW
$ 
$ ls -l
total 8
-rwxr-xr-x@ 2 hp  staff  348 12 Mar 14:28 batman
lrwxr-xr-x@ 1 hp  staff    6 12 Mar 13:35 robinDG -> batman
lrwxr-xr-x@ 1 hp  staff    6 12 Mar 13:36 robinDW -> batman
lrwxr-xr-x@ 1 hp  staff    6 12 Mar 13:35 robinTD -> batman 

5. When we run the script through these different symlinks

$ ./robinDG
And I'm robin (dick grayson)
$ ./robinTD
And I'm robin (tim drake)
$ ./robinDW
And I'm robin (damian wayne)

Each invocation produces different output, even though they're all pointing to the same executable!

How/Where do I use this

~/bin
$ ls
lrwxr-xr-x@    - hp 1 week   blender_render -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   cn -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   compress_pdf -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   convert_to_webp -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   crd -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   gacp -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   gcd -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   lcm -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   mkcd -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 8 months nvim -> /Users/hp/Developer/tools/neovim/build/bin/nvim
lrwxr-xr-x@    - hp 1 week   openssl_dec -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   openssl_enc -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   print_colors -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@    - hp 1 week   remove_ds -> /Users/hp/dotfiles/bin/utils

Practical Applications

BusyBox-Style Utilities

The popular BusyBox utility uses this exact pattern. BusyBox is a single executable that provides simplified versions of many common UNIX utilities. When symlinked as `ls`, `cp`, `mv`, etc., it behaves accordingly. The BusyBox source code shows how this is implemented in C.

Multi-Purpose Scripts

Create a single maintenance script that performs different functions based on how it's called:

#!/bin/bash
case $(basename "$0") in
  backup)
    # Backup functionality
    ;;
  restore)
    # Restore functionality
    ;;
  cleanup)
    # Cleanup functionality
    ;;
esac

Installation Scripts

A single script could handle installation, uninstallation, and updates based on symlinks named `install`, `uninstall`, and `update`. The Git Project uses a similar approach for some of its utilities.